#include "length.h"

#define _EXPECT_BYTES_LENGTH2  _EXPECT_BYTES_LENGTH * 2

namespace __LENGTH__OPERATOR
{
  typedef struct __LENGTH__INSIDE
  {
    _hfptlong data_[_EXPECT_BYTES_LENGTH2];
  }_length_in;
  
  typedef union
  {
    _length_in in;
    _length out;
  }_length_u;

  template<int N>
  class runner
  {
  public:
    static inline void run(void (*fun)(int))
    {
      fun(N);runner<N-1>::run(fun);
    }
  };
      
    
  template<>
  class runner<0>
  {
  public:
      static inline void run(void (*fun)(int))
    {
      fun(0);
    }
  };
  template<typename T>
  constexpr T _maxsizeof(T type)
  {
    return (T)(~0);
  }
  template<typename T>
  constexpr _ptlong _maxoffset(T type)
  {
    return (sizeof(T) * 8);
  }
}
    
using namespace __LENGTH__OPERATOR;

/*

inline _length operator^(const _length& a,const _length& b)
{
  _length ret;
  runner<_EXPECT_BYTES_LENGTH - 1>::run([&](int i)->void{ret.data_[i] = a.data_[i] ^ b.data_[i]});
  return ret;    
}

inline _length operator&(const _length& a,const _length& b)
{
  _length ret;
  runner<_EXPECT_BYTES_LENGTH - 1>::run([&](int i)->void{ret.data_[i] = a.data_[i] & b.data_[i];});
  return ret;  

}

inline _length operator~(const _length& len)
{
  _length ret;
  runner<_EXPECT_BYTES_LENGTH - 1>::run([&](int i)->void{ret.data_[i] = ~len.data_[i];});
  return ret;

}
inline _length operator+(const _length& lenA,const _length& lenB)
{
  _length_u a = lenA;
  _length_u b = lenB;
  return _LENGTH_PLUS(a,b);
}
inline bool operator>(const _length& lenA,const _length& lenB)
{
  return (Comparelength(lenA,lenB) == 1);
}
inline bool operator==(const _length& lenA,const _length& lenB)
{
  return (Comparelength(lenA,lenB) == 0);
}
inline bool operator<(const _length& lenA,const _length& lenB)
{
  return (Comparelength(lenA,lenB) == -1);
}
inline bool operator<=(const _length& lenA,const _length& lenB)
{
  return ((lenA < lenB) || (lenA == lenB));
}
inline bool operator>=(const _length& lenA,const _length& lenB)
{
  return ((lenA > lenB) || (lenA == lenB));
}
static _length _LENGTH_PLUS(_length_u& a,_length_u& b,_ptlong num = 0)
{
  _ptlong buff;
  return ((num < _EXPECT_BYTES_LENGTH2)
	  ? ((buff = (_ptlong)(a.in.data_[num]) + (_ptlong)(b.in.data_[num]))
	     ,((num < _EXPECT_BYTE_LENGTH2 - 1
		?a.in.data_[num + 1] += (_hfptlong)((buff - (_ptlong)(_maxsizeof((_hfptlong)0))) >> _maxoffset((_hfptlong)0)))
	       :0)
	     ,a.in.data_[num] = (_hfptlong)buff
	     ,_LENGTH_PLUS(a,b,num+1))
	  : a);
  
}

inline _length operator*(const _length& lenA,const _length& lenB)
{//Will change..
  _length len = {{0}};
  const _length_u& a = lenA;
  const _length_u& b = lenB;
  for(_ptlong i = 0 ; i < _EXPECT_BYTES_LENGTH2 ; i++)
    {
      for(_ptlong j = 0; j < _EXPECT_BYTES_LENGTH2; j++)
	{
	  _ptlong buff = (_ptlong)(a.in.data_[i]) * (_ptlong)(b.in.data_[j]);
	  len = len + ((_length)(_LEFT_MOVE((_length_u)Makelength(buff),i+j)));
	}
    }
  return len;
}

inline _length operator<<(const _length& lenA,_ptlong num)//problem
{
	if(num > sizeof(lenA) * 8)
		return Makelength<0>();
	_ptlong last = 0;
	_length ret = lenA;
	for(int i = 0 ; i < _EXPECT_BYTES_LENGTH; i++)
	{
		_ptlong _this = HIGHB(num) & ret.data_[i];
		ret.data_[i] <<= num;
		ret.data_[i] |= last;
		last = _this >> (sizeof(_ptlong) * 8 - num);
	}
	return ret;
}

inline _ptlong operator%(const _length& lenA,_ptlong num)
{
	_ptlong ans = 0;
	_length_u& k  = lenA;
	for(int i = 0; i < _EXPECT_BYTES_LENGTH2 ; i++)
		ans = ((ans << (sizeof(_hfptlong) * 8)) + k.in.data_[i]) % num;
	return ans;
}

static inline _length_u& _LEFT_MOVE(_length_u& a, _ptlong num)
{
  return (num > 0
	  ?_LEFT_MOVE(_SINGLE_LEFT_MOVE(a),num - 1)
	  :a);
}

static inline _length_u& _SINGLE_LEFT_MOVE(_length_u& a,_ptlong num = _EXPECT_BYTES_LENGTH2)
{
  return (num > 0
	  ?_SINGLE_LEFT_MOVE((a.in.data_[num] = a.in.data_[num - 1],a),num - 1)
	  :a);
}

inline _length operator++(_length& len,int) //i++
{
  _length ret = len;
  __LENGTHPLUSPLUS(len);
  return ret;
}

inline _length operator++(_length& len) // ++i
{
  return  __LENGTHPLUSPLUS(len);
}

inline static _length __LENGTHPLUSPLUS(_length& len,unsigned int num = 0) // if could be lambda?
{
  return((num < _EXPECT_BYTES_LENGTH)
	 ?((++len.data_[_EXPECT_BYTES_LENGTH - num - 1] == 0)
	   ?__LENGTHPLUSPLUS(len,num + 1)
	   :len)
	 :len
	 );
}
*/
inline _length Makelength(_ptlong num)
{
  _length ret = {{0}};
  ret.data_[0] = num;
  return ret;

}

inline _length Makelength(_ptlong num1,ptlong num2)
{
  _length ret = {{0}};
  ret.data_[0] = num2;
  ret.data_[1] = num1;
  return ret;
  
}


inline int Comparelength(const _length &A,const _length &B)
{
  return _lengthCompare(A,B,_EXPECT_BYTES_LENGTH - 1);
} 
static inline int _lengthCompare(const _length& A,const _length& B,unsigned int num)
{
  return ((num >= 0)
	  ? (A.data_[num] == B.data_[num]
	     ? lengthCompare(A,B,num- 1)
	     : (A.data_[num] < B.data_[num]
		? -1
		: 1))
	  : 0);
} 
